home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / mint / mint96sb.zoo / doc / debug.doc < prev    next >
Text File  |  1992-09-29  |  8KB  |  199 lines

  1. Writing Debuggers for MiNT
  2.  
  3. MiNT provides a special pseudo-drive, U:\PROC, in which processes appear
  4. as files. This drive may be used by debuggers much as the Unix /proc
  5. file system is used, although the specific details of implementation will
  6. be different.
  7.  
  8. The entry for a process in U:\PROC has a name like SOMEPROG.nnn, where
  9. "nnn" is a 3 digit number which is the (decimal) process ID of the process,
  10. and "SOMEPROG" is the name of the process. When opening a process, only
  11. the process ID matters; a process does *not* need to know another process'
  12. name in order to open it, only its process id. So, for example,
  13.    fd = Fopen("U:\\PROC\\FOO.032",2);
  14.    fd = Fopen("U:\\PROC\\.032",2);
  15. and
  16.    fd = Fopen("U:\\PROC\\TCSH.032",2);
  17. will all open the process with process id #32 for reading and writing,
  18. regardless of the actual name of that process.
  19.  
  20. Also, note that a process id of -1 refers to the current process, and a
  21. process id of -2 refers to the parent of the current process; thus,
  22.    fd = Fopen("U:\\PROC\\.-1",2);
  23. may always be used to open oneself.
  24.  
  25. Before a process may be debugged (or "traced") it must first be marked
  26. as a traced process, and a specific process (often its parent) must
  27. be marked as the "tracer", the program which will be notified when the
  28. traced process receives signals.
  29.  
  30. (1) If the process to be traced is started with Pexec, and the high bit
  31.     of the Pexec mode is set, then the child process begins
  32.     as a "traced" process automatically, with its parent as tracer.
  33.  
  34. Example:
  35.     childpid = Pexec(0x8000|100, "foo.prg", "", 0L);
  36.  
  37. (2) The traced process may indicate that it wishes to be traced by opening
  38.     its own entry in U:\PROC and performing an Fcntl(...PTRACESFLAGS) call
  39.     which enables tracing. In this case, the child's parent will become
  40.     the tracer automatically. Note that if the parent is not prepared to
  41.     perform debugging functions, there could be undesireable results, so
  42.     the child must be *certain* that the parent wishes to debug it; for
  43.     example, this is the case if the child and parent are both executing
  44.     from the same program image (e.g. the child was started with the
  45.     Pvfork() system call). This method of indicating tracing is quite
  46.     similar to Unix's ptrace(0,...) function.
  47.  
  48. Example:
  49.     if ((pid = Pvfork()) == 0) {
  50.         short flag = 1;
  51.     int fd;
  52.  
  53. /* here we are in the child */
  54. /* open self; see notes above */
  55.      fd = Fopen("U:\\PROC\\A.-1",2);
  56. /* perform Fcntl */
  57.     Fcntl(fd, &flag, PTRACESFLAGS);
  58. /* close self, we don't need the handle any more */
  59.     Fclose(fd);
  60. /* now overlay the child with a new program image */
  61.     Pexec(200, "foo.prg", "", 0L);
  62.     }
  63.  
  64. (3) A process may force another process to be traced by opening that process'
  65.     entry in U:\PROC, and doing an Fcntl(...PTRACESFLAGS) on it. This is
  66.     the only way in which to trace a process that is not the
  67.     child of the debugging process.
  68.  
  69. Example:
  70.     short flag = 1;
  71.  
  72. /* open process "pid" for tracing */
  73.     sprintf(name, "U:\\PROC\\A.%03d", pid);
  74.     fd = Fopen(name, 2);
  75.     Fcntl(fd, &flag, PTRACESFLAGS);
  76. /* leave fd open so that we can read and write it... */
  77.  
  78.  
  79. What Happens When A Traced Process Receives a Signal
  80.  
  81. If a traced process receives a signal (for example, a bus error), then
  82. it is placed into a STOP condition and a SIGCHLD signal is sent to
  83. the tracer. If the tracer performs a Pwait3 or Pwaitpid system call,
  84. it can then retrieve the pid of the stopped process, and the signal which
  85. caused that process to stop. For example:
  86.  
  87. #define WIFSTOPPED(x)    (((int)((x) & 0xFF) == 0x7F) && ((int)(((x) >> 8) & 0xFF) != 0))
  88. #define WSTOPSIG(x)    ((int)(((x) >> 8) & 0xFF))
  89.     unsigned long r;
  90.  
  91. /* 0x2 is a flag that indicates that we want to see stopped processes */
  92.     r = Pwait3(0x2, 0L);
  93.     if (WIFSTOPPED(r)) {
  94.     pid = r >> 16;
  95.     signal = WSTOPSIG(r);
  96.     }
  97.  
  98. The tracer can then examine the stopped process' address space and registers
  99. (using the Fread, Fwrite, and Fcntl system calls; see below) and/or make
  100. modifications to the stopped process' state. It can then restart that
  101. process with either the PTRACEGO, PTRACEFLOW, or PTRACESTEP Fcntl commands.
  102. The PTRACEGO command Fcntl(fd, &sig, PTRACEGO), where "sig" is a 16 bit
  103. integer, restarts the process. If sig == 0, then all pending signals in
  104. the traced process are cleared; otherwise, the signal represented by
  105. "sig" will be delivered to the process after it starts again. Normally,
  106. this signal would be the same one which caused the process to stop. Note
  107. that this second time the signal is delivered it will not cause the
  108. process to stop, but rather will cause whatever action is normally associated
  109. with the signal (for example, SIGKILL will kill the process).
  110.  
  111. PTRACEFLOW and PTRACESTEP are similar to PTRACEGO, but they also set some
  112. bits in the status register of the stopped process which will cause a
  113. SIGTRAP (trace trap) signal to be raised either on the next instruction
  114. to be executed (PTRACESTEP) or the next branch or jump instruction to
  115. be executed (PTRACEFLOW; this only works on a 68030 or 68040 processor).
  116. Note that if the traced process was executing a system call at the time
  117. it stopped, the trace trap signal will not take effect until the process
  118. leaves the kernel. PTRACESTEP, then, makes it possible to single step
  119. through the traced program.
  120.  
  121.  
  122. Reading and Writing the Process' Address Space, and Setting Breakpoints
  123.  
  124. Traditional debuggers for the ST have directly accessed the address space
  125. of the debugged process. This is undesireable because it assumes that
  126. both processes share a common address space, something which may not be
  127. the case if memory protection is enabled; also, in a virtual memory
  128. system logical addresses in the child and parent may be translated
  129. differently. The U:\PROC file system may be used to avoid both of these
  130. difficulties. If the debugger opens the process to be debugged for
  131. reading and writing, it may then use Fread and Fwrite system calls to
  132. transfer data (e.g. breakpoint instructions) to and from the debugged
  133. process' address space.
  134.  
  135. To read 100 bytes from address 0x0123456 in the address space of
  136. process 12, for example, one would do:
  137.     char buf[100];
  138.  
  139.     fd = Fopen("U:\\PROC\\A.012", 2);
  140.     Fseek(0x0123456L, fd, 0);
  141.     Fread(fd, 100L, buf);
  142. (Note that error checking should be performed in any real application;
  143. for example, the Fopen could very well fail if the process being
  144. opened is another user's process.)
  145.  
  146.  
  147. Reading and Writing the Process' Registers
  148.  
  149. The registers of the stopped process are also available for inspection.
  150. The PPROCADDR and PCTXTSIZE Fcntl commands are used to find the
  151. address of the process context block in the traced process' address
  152. space; the registers of the process can then be read from the address
  153. space with Fread, or written to the address space with Fwrite.
  154.  
  155. Example:
  156.     long curprocaddr;
  157.     long ctxtsize;
  158.     struct context {
  159.     long regs[15];    /* registers d0-d7, a0-a6 */
  160.     long usp;    /* user stack pointer */
  161.     short sr;    /* status register */
  162.     long pc;    /* program counter */
  163.     long ssp;    /* supervisor stack pointer */
  164.     long tvec;    /* GEMDOS terminate vector */
  165.     char fstate[216]; /* internal FPU state */
  166.     long fregs[3*8];  /* registers fp0-fp7 */
  167.     long fctrl[3];    /* FPCR/FPSR/FPIAR */
  168. /* there are some more, undocumented, fields, in the
  169.  * MiNT context structure
  170.  */
  171.     } c;
  172.  
  173. /* get the address of the process control structure */
  174.     Fcntl(fd, &curprocaddr, PPROCADDR);
  175.  
  176. /* get the size of the process context structure.
  177.  * there are 2 of these, located immediately before
  178.  * the process control structure. The first one
  179.  * is the one we're interested in (the second one
  180.  * is used by MiNT, and should never be modified).
  181.  */
  182.     Fcntl(fd, &ctxtsize, PCTXTSIZE);
  183.  
  184. /* make curprocaddr point to the first context struct */
  185.     curprocaddr -= 2*ctxtsize;
  186.  
  187. /* now read the context structure */
  188.     Fseek(curprocaddr, fd, 0);
  189.     Fread(fd, (long)sizeof(struct context), &c);
  190.  
  191. /* the various fields of c are now set up properly */
  192.    ... /* code omitted */
  193.